package com.lmn.config;

import com.lmn.oval.Pojo;
import net.sf.oval.ConstraintViolation;
import net.sf.oval.Validator;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @version 4.0
 * @describe: OVAL 校验框架的注解集成实现
 * @author: houkai
 * @Date: 2018/5/10 11:58
 */
@Aspect
@Component
@Order(2)
public class OvalImpl {

    @Pointcut("execution(public * com.lmn.*.controller..*.*(..))")
    public void ovalPointcut() {
    }


    @Before("ovalPointcut()")
    public void validate(JoinPoint joinPoint) throws IllegalAccessException {
        //声明校验器
        Validator validator = new Validator();
        //获取所有请求参数
        Object[] args = joinPoint.getArgs();
        //创建需要校验的对象（组件）的集合，用于存储信息
        List<Object> objs = new ArrayList<>();
        for (Object arg : args) {
            /*if (!(arg instanceof BaseRequest)) {
                continue;
            }*/
            getObjects(arg, objs);
            //遍历集合中的每一个对象
            for (Object obj : objs) {
                //调用框架进行校验，校验不通过的项会被存入ConstraintViolation集合中
                List<ConstraintViolation> list = validator.validate(obj);
                if (list != null && list.size() != 0) {
                    throw new RuntimeException();  //todo Fixme 配合异常使用
                    //throw new ParamException(list.get(0).getErrorCode(), list.get(0).getMessage());
                }
            }
        }
    }

    /**
     * 检查当前需要校验的对象是否存在 @Pojo 标记的属性，存在则对该属性继续进行递归检查
     * 属性为 Map 类型时，不进行校验
     * 当前对象校验完成，添加进入使用
     *
     * @param obj  当前要校验的对象信息
     * @param objs 需要校验的对象（组件）的集合，用于存储信息
     */
    private void getObjects(Object obj, List<Object> objs) throws IllegalAccessException {
        //对象为空或者为map类型不做校验
        if (obj == null || (obj instanceof Map)) {
            return;
        }
        //判断对象是否为list类型，获取list中的每一个对象进行校验
        //判断对象是否为list类型，获取list中的每一个对象进行校验
        if (obj instanceof AbstractList) {
            AbstractList list = (AbstractList) obj;
            for (Object listObj : list) {
                if (listObj instanceof String || listObj instanceof Map) {
                    continue;
                }
                getObjects(listObj, objs);
            }
            return;
        }

        objs.add(obj);

        //获取类属性
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            if ("serialVersionUID".equals(field.getName())) {
                continue;
            }
            field.setAccessible(true);
            //如果指定类型的注释存在于此元素上，则返回 true，否则返回 false。此方法主要是为了便于访问标记注释而设计的
            if (field.isAnnotationPresent(Pojo.class)) {
                getObjects(field.get(obj), objs);
            }
        }
    }
}
